package com.drestuart.autofit;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Scanner;

import org.apache.commons.cli.*;

/**
 * @author dstu
 * 
 *         \class AutoFit
 * 
 *         \brief The main class in charge of generating test classes and wiki
 *         tables.
 * 
 *         This class loads a given class and uses the reflection API to read
 *         its methods and data fields. It then uses the templates in the
 *         /bin/resources folder to put together the test class source code and
 *         the testing wiki table for FitNesse.
 */
public class AutoFit {

	/**
	 * \brief Capitalizes the first letter of the given string.
	 * 
	 * @param inText
	 *            The string to work on.
	 * @return The string with the first letter capitalized.
	 */
	public static String capitalizeFirst(String inText) {
		return inText.substring(0, 1).toUpperCase() + inText.substring(1);
	}

	/**
	 * The main method. More info here later.
	 * 
	 * @param args
	 *            Command-line arguments
	 * @throws FileNotFoundException
	 */

	public static void main(String[] args) throws FileNotFoundException {

		Options options = setupOptions();
		String classname = null;
		String classdir;
		CommandLine cl = null;
		HelpFormatter formatter = new HelpFormatter();
		CommandLineParser parser = new GnuParser();
		String usageString = "java -jar autofit.jar -c <classname> [OPTIONS]";
		AutoFit af;
		String testfile;
		String wikifile;
		String outputdir;

		// Parse command-line options
		try {
			cl = parser.parse(options, args);
		} catch (ParseException exp) {
			System.err.println("Parsing failed: " + exp.getMessage());
			formatter.printHelp(usageString, options);
			System.exit(0);
		}

		if (cl.hasOption("version")) {
			System.out.println(version);
			System.exit(0);
		}
		
		// Extract command-line options
		if (!cl.hasOption("c") || cl.hasOption("?") || cl.hasOption("h")) {
			formatter.printHelp(usageString, options);
			System.exit(0);
		}

		else {
			classname = cl.getOptionValue("c");
		}

		classdir = cl.getOptionValue("d", System.getProperty("user.dir"));

		af = new AutoFit(classdir, classname);

		if (cl.hasOption("o")) {
			outputdir = cl.getOptionValue("o");
			testfile = outputdir + af.fileSeparator + af.simpleName
					+ "FitTest.java";
			wikifile = outputdir + af.fileSeparator + af.simpleName
					+ "WikiTests.txt";
			try {
				af.setFitClassPath(testfile);
				af.setWikiPath(wikifile);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		// -t and -w override -o
		if (cl.hasOption("t")) {
			testfile = cl.getOptionValue("t");
			try {
				af.setFitClassPath(testfile);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		if (cl.hasOption("w")) {
			wikifile = cl.getOptionValue("w");
			try {
				af.setWikiPath(wikifile);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		af.generateFiles();

		// AutoFit af = new AutoFit(
		// "C:\\workspace\\autofit\\bin\\com\\drestuart\\autofit",
		// "com.drestuart.autofit.TestClass");
		// System.out.println(af.workingDir);
		// af.generateFiles();

	}

	/**
	 * Set up command-line options.
	 */
	private static Options setupOptions() {
		Options options = new Options();
		options = new Options();

		options.addOption("h", "help", false, "display this help message");
		options.addOption("?", false, "display this help message");
		
//		options.addOption("v", "version", false, "display version");
		
		@SuppressWarnings("static-access")
		Option version = OptionBuilder.withDescription("display version and exit").withLongOpt("version").create();
		options.addOption(version);

		Option classnameopt = new Option("c", "class", true,
				"the fully qualified name of the class to load <required>");
		classnameopt.setArgName("classname");
//		classnameopt.setRequired(true);
		options.addOption(classnameopt);

		Option diropt = new Option("d", "directory", true,
				"the directory containing the desired .class files");
		diropt.setArgName("directory");
		options.addOption(diropt);

		Option testfileopt = new Option("t", "testfile", true,
				"where to write out the test class file.  - for stdout");
		testfileopt.setArgName("testfile.java");
		options.addOption(testfileopt);

		Option wikifileopt = new Option("w", "wikifile", true,
				"where to write out the wiki table file.  - for stdout");
		wikifileopt.setArgName("wikifile");
		options.addOption(wikifileopt);

		Option outdiropt = new Option("o", "outputdir", true,
				"the directory to write the files out to.  -t and -w override this option.");
		outdiropt.setArgName("directory");
		options.addOption(outdiropt);
		
		

		return options;
	}

	/**
	 * \brief Check that the given path exists, and is a directory.
	 * 
	 * @param path
	 * @return Whether the path is a valid directory or not
	 */

	private static boolean validDir(String path) {
		if (path == null) {
			return false;
		}
		// Determine if the path is valid
		File dir = new File(path);

		// If the path is not a directory, or does not exist, then children will
		// be null.
		String[] children = dir.list();
		if (children == null) {
			return false;
		} else {
			return true;
		}
	}

	private String className;
	private String classPath;
	private InputStream DATA_FIELD_DECLARATION_TEMPLATE = null;
	private String fileSeparator = System.getProperty("file.separator");
	private String fitClassPath;
	private String lineSeparator = System.getProperty("line.separator");
	private String pathSeparator = System.getProperty("path.separator"); 
	private final String RESOURCE_DIR = "resources";
	private InputStream SET_DATA_METHOD_TEMPLATE = null;
	private String simpleName;
	private InputStream TEST_METHOD_TEMPLATE = null;
	private InputStream TEST_SOURCE_FILE_TEMPLATE = null;
	private InputStream TEST_WIKI_PAGE_TEMPLATE = null;
	private Class<?> theClass;
	private String wikiPath;
	private String workingDir;
	private static final String version = "AutoFit version 1.0.0\nAuthor: Dan Stuart, http://code.google.com/p/autofit/";
	
	
	/**
	 * \brief Default constructor.
	 * 
	 * This constructor doesn't load a class, but doesn't fail either. If you
	 * already know the class you want, it is better to use \ref AutoFit(String)
	 * instead. If you use this constructor, make sure to load a class later
	 * with \ref LoadClass(String).
	 * 
	 * @throws FileNotFoundException
	 * 
	 * @see loadClass(String)
	 * @see AutoFit(String)
	 */
	public AutoFit() throws FileNotFoundException {
		this(null, null);
	}

	/**
	 * 
	 * \brief Constructor.
	 * 
	 * Loads the given class and does some other setup.
	 * 
	 * @param classPath
	 *            The full path to the class file to load.
	 * @throws FileNotFoundException
	 */
	public AutoFit(String classPath, String className)
			throws FileNotFoundException {

		if (classPath != null && className != null) {
			this.className = className;
			this.classPath = classPath;
			if (!validDir(classPath)) {
				System.out.println("Invalid class path: " + classPath);
				throw new FileNotFoundException(classPath);
			}
			// / Add the path to Java's classpath
			this.addToClassPath(classPath);
			// / Load the class file
			this.loadClass(className);
			this.workingDir = System.getProperty("user.dir");
			try {
				this.setFitClassPath();
				this.setWikiPath();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		/// Open the template files
		try {
			this.DATA_FIELD_DECLARATION_TEMPLATE = AutoFit.class
			.getClass()
			.getResource(
					"/" + RESOURCE_DIR + "/"
					+ "DataFieldDeclarationTemplate.txt").openStream();
			this.SET_DATA_METHOD_TEMPLATE = AutoFit.class
			.getClass()
			.getResource(
					"/" + RESOURCE_DIR + "/" + "SetDataMethodTemplate.txt")
					.openStream();
			this.TEST_METHOD_TEMPLATE = AutoFit.class
			.getClass()
			.getResource(
					"/" + RESOURCE_DIR + "/" + "TestMethodTemplate.txt")
					.openStream();
			this.TEST_SOURCE_FILE_TEMPLATE = AutoFit.class
			.getClass()
			.getResource(
					"/" + RESOURCE_DIR + "/" + "TestSourceFileTemplate.txt")
					.openStream();
			this.TEST_WIKI_PAGE_TEMPLATE = AutoFit.class
			.getClass()
			.getResource(
					"/" + RESOURCE_DIR + "/" + "TestWikiPageTemplate.txt")
					.openStream();
		}
		catch (IOException e) {
			e.printStackTrace();
		}

	}

	/**
	 * 
	 * \brief Add the given path to Java's classpath, so that we can find the
	 * .class file we're looking for.
	 * 
	 * @param path
	 *            the directory path to add.
	 * @throws FileNotFoundException
	 * 
	 */
	private void addToClassPath(String path) throws FileNotFoundException {
		// / The current classpath
		String classpath = System.getProperty("java.library.path");
		String newClassPath;

		if (!validDir(path)) {
			throw new FileNotFoundException(path);
		}

		// Add the path to the classpath
		newClassPath = classpath + this.pathSeparator + path;
		System.setProperty("java.class.path", newClassPath);

	}

	/**
	 * \brief Generates the testing file and the wiki table at the default
	 * paths.
	 * 
	 * @see generateFiles(String, String)
	 */
	public void generateFiles() {
		this.generateFiles(this.fitClassPath, this.wikiPath);
	}

	/**
	 * \brief Generates the testing file and the wiki table.
	 * 
	 * @param sourcePath
	 *            Where to place the testing code
	 * @param wikiPath
	 *            Where to place the wiki code
	 * 
	 * @see generateSourceFile(String)
	 * @see generateWikiFile(String)
	 */
	public void generateFiles(String sourcePath, String wikiPath) {
		generateSourceFile(sourcePath);
		generateWikiFile(wikiPath);
	}

	/**
	 * \brief Generates the testing file at the default location.
	 * 
	 * @see generateSourceFile(String)
	 */

	public void generateSourceFile() {
		generateSourceFile(this.fitClassPath);
	}

	/**
	 * \brief Generates the testing file.
	 * 
	 * Calls the makeFitClass(ArrayList<Method>, ArrayList<Field>) to generate
	 * the testing code, and writes it out to the given file.
	 * 
	 * @param sourcePath
	 *            The path to write out the testing file.
	 * 
	 * @see makeFitClass(ArrayList<Method>, ArrayList<Field>)
	 */
	public void generateSourceFile(String sourcePath) {

		ArrayList<Method> methods;
		ArrayList<Field> fields;
		String sourceFileCode = null;

		try {
			methods = this.getMethods();
			fields = this.getFields();
			sourceFileCode = this.makeFitClass(methods, fields);
		} catch (NoClassLoadedException e) {
			e.printStackTrace();
		} catch (BlankPropertiesException e) {
			e.printStackTrace();
		}

		if (sourceFileCode != null) {
			if (sourcePath.equals("-")) {
				System.out.println("Fit test code" + this.lineSeparator + "=============");
				System.out.println(sourceFileCode);
				System.out.println();
			}
			else {
			// Write out source file
				try {
					BufferedWriter out = new BufferedWriter(new FileWriter(
							sourcePath));
					out.write(sourceFileCode);
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

	}

	/**
	 * \brief Generates the wiki testing tables into the default file.
	 * 
	 * @see generateWikiFile(String)
	 */

	public void generateWikiFile() {
		generateWikiFile(this.wikiPath);
	}

	/**
	 * \brief Generates the wiki testing tables into the given file.
	 * 
	 * Calls the makeWikiPage(ArrayList<Method>, ArrayList<Field>) method to
	 * create the FitNesse testing tables for our class, and writes out to the
	 * given file.
	 * 
	 * @param wikiPath
	 *            The path to write out the tables to.
	 * @see makeWikiPage(ArrayList<Method>, ArrayList<Field>)
	 */
	public void generateWikiFile(String wikiPath) {

		ArrayList<Method> methods;
		ArrayList<Field> fields;
		String wikiPageCode = null;

		try {
			methods = this.getMethods();
			fields = this.getFields();
			wikiPageCode = this.makeWikiPage(methods, fields);
		} catch (NoClassLoadedException e) {
			e.printStackTrace();
		} catch (BlankPropertiesException e) {
			e.printStackTrace();
		}

		if (wikiPageCode != null) {
			if (wikiPath.equals("-")) {
				System.out.println("Wiki page code" + this.lineSeparator + "==============");
				System.out.println(wikiPageCode);
				System.out.println();
			}
			else {
				try {
					BufferedWriter out = new BufferedWriter(
							new FileWriter(wikiPath));
					out.write(wikiPageCode);
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

	}

	/**
	 * \brief Formats a list of dummy variables, named by class with a prefix,
	 * to pass into a function.
	 * 
	 * @param params
	 *            A Class[] array of the required types.
	 * @param prefix
	 *            The dummy variable prefix. Sould be something clear, like
	 *            "dummy" or "fake".
	 * @return The dummy variable names, joined by commas.
	 * 
	 * @See makeFitClass(ArrayList<Method>, ArrayList<Field>)
	 */
	public String getDummyArgs(Class<?>[] params, String prefix) {
		String retStr = "";
		String dummyVar;

		for (Class<?> c : params) {
			if (!retStr.equals("")) {
				retStr += ", ";
			}

			dummyVar = prefix + capitalizeFirst(c.getSimpleName());
			retStr += dummyVar;
		}

		return retStr;
	}

	public String getDummyArgDeclarations(Class<?>[] params, String prefix) {
		String retStr = "";
		String dummyVar;

		for (Class<?> c : params) {
			if (!retStr.equals("")) {
				retStr += ", ";
			}

			dummyVar = c.getSimpleName() + " " + prefix
					+ capitalizeFirst(c.getSimpleName()) + " = new "
					+ c.getSimpleName() + "();";
			retStr += dummyVar;
		}

		return retStr;
	}

	/**
	 * \brief Generates declarations for dummy versions of variables.
	 * 
	 * This method is used for setting up testing method declarations.
	 * 
	 * @param params
	 *            A Class[] array of the required types.
	 * @param prefix
	 *            The dummy variable prefix. Sould be something clear, like
	 *            "dummy" or "fake".
	 * @return The dummy variable declarations, joined by commas.
	 * 
	 * @See makeFitClass(ArrayList<Method>, ArrayList<Field>)
	 */
	public String getDummyParams(Class<?>[] params, String prefix) {
		String retStr = "";
		String className;
		String dummyVar;

		for (Class<?> c : params) {
			if (!retStr.equals("")) {
				retStr += ", ";
			}

			className = c.getSimpleName();
			dummyVar = prefix + capitalizeFirst(className);
			retStr += className + " " + dummyVar;
		}

		return retStr;
	}

	/**
	 * 
	 * \brief Gets an ArrayList<Field> of all the public, non-@NoFit fields in
	 * the class.
	 * 
	 * @return ArrayList<Field> of all valid fields.
	 * @throws NoClassLoadedException
	 *             if no class has been loaded.
	 * @see getMethods()
	 */
	private ArrayList<Field> getFields() throws NoClassLoadedException {
		if (this.theClass != null) {
			Field[] decFields = theClass.getDeclaredFields();
			ArrayList<Field> fields = new ArrayList<Field>();

			for (Field f : decFields) {
				if (f.getAnnotation(NoFit.class) == null) {
					fields.add(f);
				}
			}
			return fields;
		} else {
			throw new NoClassLoadedException();
		}
	}

	/**
	 * 
	 * \brief Gets an ArrayList<Method> of all the public, non-@NoFit methods in
	 * the class.
	 * 
	 * @return ArrayList<Method> of all valid methods.
	 * @throws NoClassLoadedException
	 *             if no class has been loaded.
	 * @see getFields()
	 */
	private ArrayList<Method> getMethods() throws NoClassLoadedException {
		if (this.theClass != null) {
			Method[] decMethods = theClass.getDeclaredMethods();
			ArrayList<Method> methods = new ArrayList<Method>();
			NoFit ann;

			for (Method m : decMethods) {
				int perms = m.getModifiers();
				ann = m.getAnnotation(NoFit.class);
				if ((ann == null) && (Modifier.isPublic(perms))) {
					methods.add(m);
				}
			}
			return methods;
		} else {
			throw new NoClassLoadedException();
		}
	}

	/**
	 * 
	 * \brief Load the named class, and set it as the class that AutoFit will
	 * work on.
	 * 
	 * @param classname
	 *            The fully qualified name of the class to load
	 */

	public void loadClass(String classname) {
		if (classname == null) {
			return;
		}

		try {
			this.theClass = Class.forName(classname);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		className = this.theClass.getName();
		simpleName = this.theClass.getSimpleName();
	
	}

	/**
	 * \brief Generates the testing code. This is one of the two workhorse
	 * methods.
	 * 
	 * This method does the work of generating the FitNesse testing code for our
	 * class by reading all of its methods and fields and filling these data
	 * into various templates. The templates are then assembled into a master
	 * template, which becomes the source file for our testing class.
	 * 
	 * @param methods
	 *            The methods to generate testing code for
	 * @param fields
	 *            The fields to generate testing code for
	 * @return The testing code
	 * @throws BlankPropertiesException
	 * @see makeWikiPage(ArrayList<Method>, ArrayList<Field>)
	 * 
	 */
	public String makeFitClass(ArrayList<Method> methods,
			ArrayList<Field> fields) throws BlankPropertiesException {

		// Template objects
		Template DataFieldTmp = null;
		Template SetDataMethodTmp = null;
		Template testMethodTmp = null;
		Template testSourceFileTmp = null;

		DataFieldTmp = new Template(new Scanner(
				this.DATA_FIELD_DECLARATION_TEMPLATE).useDelimiter("\\Z")
				.next());
		SetDataMethodTmp = new Template(new Scanner(
				this.SET_DATA_METHOD_TEMPLATE).useDelimiter("\\Z").next());
		testMethodTmp = new Template(new Scanner(
				this.TEST_METHOD_TEMPLATE).useDelimiter("\\Z").next());
		testSourceFileTmp = new Template(new Scanner(
				this.TEST_SOURCE_FILE_TEMPLATE).useDelimiter("\\Z").next());

		// Set up data field declarations
		String fieldDeclarations = "";
		String dec = "";
		String security = "";
		for (Field f : fields) {
			security = f.toString().split(" ")[0]; // Gets the public, private,
			// or protected status of
			// this field.

			dec = security + " " + f.getType().getSimpleName() + " "
					+ f.getName();
			DataFieldTmp.set("Declaration", dec);
			fieldDeclarations += DataFieldTmp.fillIn(true) + this.lineSeparator;
			DataFieldTmp.clear();
		}

		// Set up data field setter declarations
		String fieldSetterDeclarations = "";
		String fieldName = "";
		String setFuncName = "";
		for (Field f : fields) {
			fieldName = f.getName();
			SetDataMethodTmp.set("FieldName", fieldName);
			SetDataMethodTmp.set("FieldType", f.getType().getSimpleName());

			setFuncName = "set" + capitalizeFirst(fieldName);
			SetDataMethodTmp.set("SetFieldFuncName", setFuncName);
			fieldSetterDeclarations += SetDataMethodTmp.fillIn(true);
			SetDataMethodTmp.clear();

		}

		// Set up method testing declarations
		String testMethodDeclarations = "";
		String methodName = "";
		String methodRetType = "";
		Class<?>[] methodParams;
		for (Method m : methods) {
			methodName = m.getName();
			methodRetType = m.getReturnType().getSimpleName();
			methodParams = m.getParameterTypes();

			testMethodTmp.set("MethodName", methodName);
			testMethodTmp.set("MethodReturnType", methodRetType);
			testMethodTmp.set("TestMethodName",
					"fit" + capitalizeFirst(methodName));
			// testMethodTmp.set("TestMethodParams",
			// this.getDummyParams(methodParams, "dummy"));
			testMethodTmp.set("TestMethodArgDeclarations",
					this.getDummyArgDeclarations(methodParams, "dummy"));
			testMethodTmp.set("TestMethodArgs",
					this.getDummyArgs(methodParams, "dummy"));
			testMethodTmp.set("ClassName", this.simpleName);
			testMethodTmp.set("ClassInstanceName", this.simpleName + "Inst");
			testMethodDeclarations += testMethodTmp.fillIn(true);
			testMethodTmp.clear();
		}

		// Set up source file template
		String sourceFileCode;
		testSourceFileTmp.set("QualifiedClassName", this.className);
		testSourceFileTmp.set("ClassName", this.simpleName);
		testSourceFileTmp.set("fieldDeclarations", fieldDeclarations);
		testSourceFileTmp.set("fieldSetterDeclarations",
				fieldSetterDeclarations);
		testSourceFileTmp.set("testMethodDeclarations", testMethodDeclarations);

		sourceFileCode = testSourceFileTmp.fillIn(true);

		return sourceFileCode;

	}

	/**
	 * \brief Generates the wiki testing tables. This is one of the two
	 * workhorse methods.
	 * 
	 * This method does the work of generating the FitNesse wiki tables for our
	 * class by reading all of its methods and fields and filling these data
	 * into a template.
	 * 
	 * @param methods
	 *            The methods to generate testing code for
	 * @param fields
	 *            The fields to generate testing code for
	 * @return The testing code
	 * @throws BlankPropertiesException
	 * @see makeFitClass(ArrayList<Method>, ArrayList<Field>)
	 * 
	 */
	public String makeWikiPage(ArrayList<Method> methods,
			ArrayList<Field> fields) throws BlankPropertiesException {
		String wikiPageCode = "";
		String VariableFields = "|";
		String FieldCode = "";
		String MethodCalls = "";
		String packageName;
		int numFieldsMethods = methods.size() + fields.size();
		Template wikiPageTmp = null;

		wikiPageTmp = new Template(new Scanner(
				this.TEST_WIKI_PAGE_TEMPLATE).useDelimiter("\\Z").next());

		// Set up data
		for (Field f : fields) {
			FieldCode = FieldCode.concat("|" + f.getName());
		}

		for (Method m : methods) {
			MethodCalls = MethodCalls.concat("fit"
					+ capitalizeFirst(m.getName()) + "?|");
		}

		for (int i = 1; i < numFieldsMethods; i++) {
			VariableFields = VariableFields.concat("\t|");
		}
		VariableFields = VariableFields.concat("\t|" + this.lineSeparator);

		packageName = this.className.substring(0,
				this.className.lastIndexOf("."));

		// Set up template
		wikiPageTmp.set("ClassName", this.simpleName + "FitTest");
		wikiPageTmp.set("PackageName", packageName);
		wikiPageTmp.set("VariableFields", VariableFields);
		wikiPageTmp.set("FieldCode", FieldCode);
		wikiPageTmp.set("MethodCalls", MethodCalls);
		wikiPageTmp.set("FilePath", "!path " + this.workingDir.replace("\\", "\\\\"));
		
		if (!this.classPath.equals(this.workingDir)) {
			wikiPageTmp.set("ClassPath", "!path " + this.classPath.replace("\\", "\\\\"));
		}
		else
		{
			wikiPageTmp.set("ClassPath", "");
		}

		wikiPageCode = wikiPageTmp.fillIn(true);

		return wikiPageCode;
	}

	/**
	 * A testing method which prints out the methods of the class.
	 */
	@SuppressWarnings("unused")
	private void printMethods() {
		try {
			for (Method m : this.getMethods()) {
				System.out.println(m.toString());
				for (Annotation ann : m.getDeclaredAnnotations()) {
					System.out.println(ann.toString());
				}
				System.out.println();

			}
		} catch (NoClassLoadedException e) {
			e.printStackTrace();
		}
	}

	/**
	 * \brief Sets the testing class file to be written out to the working
	 * directory.
	 * @throws IOException 
	 * 
	 * @see setFitClassPath(String)
	 */
	public void setFitClassPath() throws IOException {
		setFitClassPath(workingDir + fileSeparator + simpleName
				+ "FitTest.java");
	}

	/**
	 * 
	 * \brief Set the path that the testing class file will be written out to.
	 * 
	 * The file name is the simple name of the loaded class, appended with
	 * "FitTest.java".
	 * 
	 * @param path
	 *            the path to write to.
	 * @throws IOException 
	 */
	public void setFitClassPath(String path) throws IOException {
		String dir = path.substring(0, path.lastIndexOf(this.fileSeparator));
		if (!validDir(dir)) {
			if ( new File(dir).mkdirs() ) {
				System.out.println("Created directory: " + dir);
			}
			else {
				throw new IOException("Failed to create directory: " + dir);
			}
		}
		this.fitClassPath = path;
	}

	/**
	 * \brief Sets the wiki table file to be written out to the working
	 * directory.
	 * @throws IOException 
	 * 
	 * @see setWikiPath(String)
	 */
	public void setWikiPath() throws IOException {
		setWikiPath(workingDir + fileSeparator + simpleName + "WikiTests.txt");
	}

	/**
	 * 
	 * \brief Set the path that the wiki table file will be written out to.
	 * 
	 * The file name is the simple name of the loaded class, appended with
	 * "WikiTests.txt".
	 * 
	 * @param path
	 *            the path to write to.
	 * @throws IOException 
	 */
	public void setWikiPath(String path) throws IOException {
		String dir = path.substring(0, path.lastIndexOf(this.fileSeparator));
		if (!validDir(dir)) {
			if ( new File(dir).mkdirs() ) {
				System.out.println("Created directory: " + dir);
			}
			else {
				throw new IOException("Failed to create directory: " + dir);
			}
		}
		this.wikiPath = path;
	}
}
